1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.ninjasoft.macpackager;
18
19 import java.io.*;
20 import java.util.*;
21
22 /***
23 * Ant task that will create a Mac OS X application bundle.
24 * This task expects a nested fileset of *.jar files
25 *
26 * Requires: jar files (in nested fileset)
27 * Version - Application version
28 * InfoString - Name, version, copyright information
29 * Icon file - *.icns file containing application icon(s)
30 * VM Options - Optional things to pass to the VM (name in app menu, shiny metal look, etc)
31 * mainclass - main class
32 *
33 * Derives: BundleIdentifier: from mainclass + version
34 *
35 * @author Brian Enigma
36 */
37 public MacPackager {/package-summary.html">class MacPackager {
38 private String appName = null;
39 private String version = null;
40 private String infoString = null;
41 private String icon = null;
42 private String vmOptions = null;
43 private String mainClass = null;
44
45 private InfoBuilder builder = new InfoBuilder()/package-summary.html">ong> InfoBuilder builder = new InfoBuilder();
46 private String appLocation;
47 private Vector jarFiles = new Vector();
48
49
50 private static final String appExt = "";
51
52 public void buildApplication() throws PackageException {
53
54 validate();
55
56 builder.setBundleName(this.appName);
57 builder.setVersion(this.version);
58 builder.setInfoString(this.infoString);
59 builder.setVmOptions(this.vmOptions);
60 builder.setMainClass(this.mainClass);
61 for (Iterator i = this.jarFiles.iterator(); i.hasNext(); )
62 builder.addJarFile(i.next().toString());
63 String target = "target/" + appName + appExt;
64 if (new File(target).exists()) {
65 System.out.println("Removing existing application " + target);
66 deleteRecursive(target);
67 }
68 if (new File(target).exists())
69 thPackageException(/package-summary.html">row new PackageException("Unable to delete " + target);
70
71 mkdirs();
72
73 copyFiles();
74
75 createFiles();
76
77 tagFolder();
78 System.out.println("Application bundle ready");
79 }
80
81 private void deleteRecursive(String s) {
82 if ((s == null) || (s.length() == 0) || s.equals(".") || s.equals(".."))
83 return;
84 File f = new File(s);
85 if (f.isDirectory()) {
86 String files[] = f.list();
87 for (int i=0; i<files.length; i++)
88 deleteRecursive(s + "/" + files[i]);
89 f.delete();
90 } else {
91 f.delete();
92 }
93 }
94
95 /***
96 * Create the target directories
97 * @throws BuildException
98 */
99 private void mkdirs() throws PackageException {
100 appLocation = "target/" + appName + appExt + "/Contents/MacOS";
101 File d = new File(appLocation);
102 if (!d.mkdirs())
103 thPackageException(/package-summary.html">row new PackageException("Unable to create folder " + d.getPath());
104 d = new File("target/" + appName + appExt + "/Contents/Resources/Java");
105 if (!d.mkdirs())
106 thPackageException(/package-summary.html">row new PackageException("Unable to create folder " + d.getPath());
107 }
108
109 private void copyFiles() throws PackageException {
110
111 File s = new File("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Resources/MacOS/JavaApplicationStub");
112 File d = new File("target/" + appName + appExt + "/Contents/MacOS/JavaApplicationStub");
113 copy(s, d);
114 String[] command = new String[] {
115 "/bin/chmod",
116 "0755",
117 d.getPath()
118 };
119
120 try{
121 Process p = Runtime.getRuntime().exec(command);
122 int rc = p.waitFor();
123 if (rc != 0)
124 PackageException(/package-summary.html">g>throw new PackageException("chmod of JavaApplicationStub returned error code " + rc);
125 }catch(IOException ioe){
126 thPackageException(/package-summary.html">row new PackageException("chmod of JavaApplicationStub threw an IOException");
127 }catch(InterruptedException ie){
128 thPackageException(/package-summary.html">row new PackageException("chmod of JavaApplicationStub threw an InterruptedException");
129 }
130
131 s = new File(icon);
132 d = new File("target/" + appName + appExt + "/Contents/Resources/icon.icns");
133 copy(s, d);
134
135 for (Iterator i = this.jarFiles.iterator(); i.hasNext(); ) {
136 s = new File(i.next().toString());
137 d = new File("target/" + appName + appExt + "/Contents/Resources/Java/" + s.getName());
138 copy(s, d);
139 }
140 }
141
142 private void createFiles() throws PackageException {
143 try{
144 File f = new File("target/" + appName + appExt + "/Contents/PkgInfo");
145 FileOutputStream out = new FileOutputStream(f);
146 out.write("APPL????".getBytes());
147 out.close();
148 }catch(IOException e){
149 thPackageException(/package-summary.html">row new PackageException("Problem creating PkgInfo file: " + e.toString());
150 }
151 try{
152 File f = new File("target/" + appName + appExt + "/Contents/Info.plist");
153 FileOutputStream out = new FileOutputStream(f);
154 out.write(builder.getXml().getBytes());
155 out.close();
156 }catch(IOException e){
157 thPackageException(/package-summary.html">row new PackageException("Problem creating Info.plist XML file: " + e.toString());
158 }
159
160 }
161
162 /***
163 * Copy a source file to a destination file
164 * @param source
165 * @param destination
166 * @throws BuildException
167 */
168 private void copy(File source, File destination) throws PackageException {
169 try{
170 FileInputStream in = new FileInputStream(source);
171 FileOutputStream out = new FileOutputStream(destination);
172 byte[] buff = new byte[1024];
173 int len;
174 while ((len = in.read(buff)) > 0)
175 out.write(buff, 0, len);
176 in.close();
177 out.close();
178 }catch(IOException e){
179 thPackageException(/package-summary.html">row new PackageException("Unable to copy " + source.getPath() + " to " + destination.getPath() + " :: " + e.toString());
180 }
181 }
182
183 /***
184 * Tag a folder as a bundle
185 * @throws BuildException
186 */
187 private void tagFolder() throws PackageException {
188 String[] command = new String[] {
189 "/Developer/Tools/SetFile",
190 "-a",
191 "B",
192 "target/" + appName + appExt
193 };
194
195 try{
196 Process p = Runtime.getRuntime().exec(command);
197 int rc = p.waitFor();
198 if (rc != 0)
199 PackageException(/package-summary.html">g>throw new PackageException("chmod of JavaApplicationStub returned error code " + rc);
200 }catch(IOException ioe){
201 thPackageException(/package-summary.html">row new PackageException("chmod of JavaApplicationStub threw an IOException");
202 }catch(InterruptedException ie){
203 thPackageException(/package-summary.html">row new PackageException("chmod of JavaApplicationStub threw an InterruptedException");
204 }
205 }
206
207 /***
208 * Check that all of the required attributes have been set, source files exist, and
209 * required executables exist.
210 *
211 * @throws BuildException
212 */
213 public void validate() throws PackageException {
214 if (appName == null) throw new PackageException("appName attribute required");
215 if (version == null) throw new PackageException("version attribute required");
216 if (infoString == null) throw new PackageException("infoString attribute required");
217 if (icon == null) throw new PackageException("icon attribute required");
218 if (vmOptions == null) vmOptions = "";
219 if (mainClass == null) throw new PackageException("mainClass attribute required");
220 if (jarFiles.size() == 0) throw new PackageException("There should be at least one jar file. Use a nested FileSet.");
221 File check = new File(icon);
222 if (!check.exists())
223 thPackageException(/package-summary.html">row new PackageException("Unable to locate icon file " + icon);
224 check = new File("/bin/chmod");
225 if (!check.exists())
226 thPackageException(/package-summary.html">row new PackageException("Unable to locate /bin/chmod");
227 check = new File("/Developer/Tools/SetFile");
228 if (!check.exists())
229 thPackageException(/package-summary.html">row new PackageException("/Developer/Tools/SetFile command does not exist. Please install the OS X Developer Tools.");
230 }
231
232
233
234 public void addJar(String jar) {
235 this.jarFiles.add(jar);
236 }
237
238 public void setIcon(String icon) {
239 this.icon = icon;
240 }
241 public void setInfoString(String infoString) {
242 this.infoString = infoString;
243 }
244 public void setMainClass(String mainClass) {
245 this.mainClass = mainClass;
246 }
247 public void setVersion(String version) {
248 this.version = version;
249 }
250 public void setVmOptions(String vmOptions) {
251 this.vmOptions = vmOptions;
252 }
253 public void setAppName(String appName) {
254 this.appName = appName;
255 }
256 }